package netvethpairs

// github有这么个提问题，让我给解决了，哈哈哈!!!
// https://github.com/vishvananda/netlink/issues/422
import (
	"log"
	"net"
	"runtime"

	"github.com/vishvananda/netlink"
	"github.com/vishvananda/netns"
)

type ovsFabric struct {
}

func (f *ovsFabric) Run(name string, guestMAC net.HardwareAddr) error {
	la := netlink.NewLinkAttrs()
	la.Name = name // "veth0"
	la.HardwareAddr = guestMAC
	guestIfc := &netlink.Veth{
		LinkAttrs: la,
		PeerName:  "veth1",
	}
	if err := netlink.LinkAdd(guestIfc); err != nil {
		return err
	}

	if err := f.pushIfcToNewNetns(guestIfc); err != nil {
		// this is the error that gets returned
		return err
	}
	// other configuration (set links up, assign IP addr, etc...)
	return nil
}

func (f *ovsFabric) pushIfcToNewNetns(ifc netlink.Link) error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	rootNetns, err := netns.Get()
	if err != nil {
		return err
	}
	defer func() {
		rootNetns.Close()
	}()

	//ifcName := ifc.Attrs().Name

	// this call not only creates a new netns, but also sets it as the current
	// OS thread netns.
	guestNetns, err := netns.New()
	if err != nil {
		return err
	}
	defer func() {
		guestNetns.Close()

		// reset rootNetns for the OS thread
		if err := netns.Set(rootNetns); err != nil {
			rootNetns.Close()

			//glog.Errorf("failed to reset root netns: goroutine is terminating: %s", err.Error())
			runtime.Goexit()
		}
	}()

	// !+[SwitchBack]
	// !!! Notice that when you all netns.New(), the NetNamespace switched to the new one.
	// !!! At this time, in new NetNamespace, you cannot find "ifc" link, because you create it in old NetNamespace.
	// !!! So you should switch the NetNamespace back to the old one.
	if err := netns.Set(rootNetns); err != nil {
		log.Fatalln("Help...")
	}
	// !-[SwitchBack]

	err = netlink.LinkSetNsFd(ifc, int(guestNetns))
	if err != nil {
		// this is the error that gets returned
		return err
	}

	return nil
}
